/*
  Copyright (c) 2010
  The Regents of the University of Michigan
  All Rights Reserved

  Permission is granted to use, copy, create derivative works, and
  redistribute this software and such derivative works for any purpose,
  so long as the name of the University of Michigan is not used in
  any advertising or publicity pertaining to the use or distribution
  of this software without specific, written prior authorization. If
  the above copyright notice or any other identification of the
  University of Michigan is included in any copy of any portion of
  this software, then the disclaimer below must also be included.

  This software is provided as is, without representation or warranty
  of any kind either express or implied, including without limitation
  the implied warranties of merchantability, fitness for a particular
  purpose, or noninfringement.  The Regents of the University of
  Michigan shall not be liable for any damages, including special,
  indirect, incidental, or consequential damages, with respect to any
  claim arising out of or in connection with the use of the software,
  even if it has been or is hereafter advised of the possibility of
  such damages.
*/

#ifndef SERIALIZATION_H
#define SERIALIZATION_H

#include "constants.h"
#include "contact.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


struct ClearviewInstance;

/* Version 22 changed the wire format that was constant for everything
   except stats since version 17, so we have to move with it. */

#define MIN_SUPPORTED_PROTOCOL_VERSION 22
#define SUPPORTED_PROTOCOL_VERSION 22 

enum Action {
  ACT_REQUEST_PING = 1024,
  ACT_REPLY_PING = 1025,
  ACT_REQUEST_STORE = 1026,
  ACT_REPLY_STORE = 1027,
  ACT_REQUEST_FIND_NODE = 1028,
  ACT_REPLY_FIND_NODE = 1029,
  ACT_REQUEST_FIND_VALUE = 1030,
  ACT_REPLY_FIND_VALUE = 1031,
  ACT_REPLY_ERROR = 1032,
  ACT_REPLY_STATS = 1033,
  ACT_REQUEST_STATS = 1034,
  ACT_DATA = 1035,
/* We ignore key blocking.
  ACT_REQUEST_KEY_BLOCK = 1036,
  ACT_REPLY_KEY_BLOCK = 1037
*/
};

enum NodeType {
  BOOTSTRAP = 0,
  ORDINARY = 1,
  UNKNOWN = 0xFFFFFFFF
};

enum ValueFlags {
  SINGLE_VALUE = 0,
  DOWNLOADING = 1,
  SEEDING = 2,
  MULTI_VALUE = 4,
  STATS = 8
};

enum ErrorType {
  WRONG_ADDRESS = 1,
  KEY_BLOCKED = 2
};

struct VivaldiCoordinates {
  /* TODO(swolchok): Implement Vivaldi coordinates? */
};

struct VuzeKey {
  /* length assumed to be KEY_SIZE always */
  uint8_t bytes[KEY_SIZE];  /* stored big-endian. */
};


struct Contact {
  // contact type ignored; must be UDP = 1
  uint8_t contact_protocol_version;
  struct ContactAddress addr;
};

#define CONTACT_NET_SIZE (1+1+CONTACT_ADDRESS_NET_SIZE)


struct TransportValue {
  uint32_t version;
  // uint64_t creation ignored.
  uint16_t value_bytes_count;
  char *value_bytes;
  struct ContactAddress originator;  // Actually a Contact on the wire.
  //uint8_t flags ignored.
};


static inline void transport_value_free_data(struct TransportValue *victim) {
  g_free(victim->value_bytes);
  victim->value_bytes = NULL;
}


struct TransportValueGroup {
  uint16_t values_count;
  struct TransportValue *values;
};


static inline void
transport_value_group_init(struct TransportValueGroup *g,
                           uint16_t values_count) {
  g->values_count = values_count;
  g->values = g_malloc0(sizeof(struct TransportValue) * values_count);
}


static inline void
transport_value_group_cleanup(struct TransportValueGroup *v) {
  int ii;
  for (ii = 0; ii != v->values_count; ++ii) {
    transport_value_free_data(&v->values[ii]);
  }

  g_free(v->values);
}


struct RequestPacketHeader {
  uint64_t connection_id;
  uint32_t action;
  uint32_t transaction_id;
  uint8_t protocol_version;
  uint8_t vendor_id;
  uint32_t network_id;
  uint8_t local_protocol_version;
  struct ContactAddress node_address;
  uint32_t instance_id;
  uint64_t time;
};

#define REQUEST_HEADER_SIZE (8+4+4+1+1+4+1+CONTACT_ADDRESS_NET_SIZE+4+8)

struct ReplyPacketHeader 
{
  uint32_t action;
  uint32_t transaction_id;
  uint64_t connection_id;
  uint8_t protocol_version;
  uint8_t vendor_id;
  uint32_t network_id;
  uint32_t instance_id;
};

#define REPLY_HEADER_SIZE (4+4+8+1+1+4+4)

struct RequestPingPacket {
  struct RequestPacketHeader header;
};

struct ReplyPingPacket {
  struct ReplyPacketHeader header;
/* We ignore Vivaldi. Remember to write out bogus Vivaldi coords in the
   send function. */
  struct VivaldiCoordinates vivaldi;
};


#define MAX_KEYS_PER_PACKET 255

struct RequestStorePacket {
  struct RequestPacketHeader header;
  uint32_t spoof_id;
  uint8_t keys_count;
  char **keys;
  uint8_t value_groups_count;
  struct TransportValueGroup *value_groups;
};

// Free the members of req, but NOT req itself.
void store_request_free_data(struct RequestStorePacket *req);


struct ReplyStorePacket {
  struct ReplyPacketHeader header;
  uint8_t diversifications_length;
  uint8_t *diversifications;
};

struct RequestFindNodePacket {
  struct RequestPacketHeader header;
  // id_length byte ignored -- we'll always send 20.
  uint8_t id[KEY_SIZE];
  uint32_t node_status;
  // dht_size ignored.
};

#define REQUEST_FIND_NODE_SIZE (REQUEST_HEADER_SIZE + 1 + KEY_SIZE + 4 + 4)

struct ReplyFindNodePacket {
  struct ReplyPacketHeader header;
  uint32_t spoof_id;
  enum NodeType node_type; // 4 bytes!
  uint32_t dht_size;  // We don't send a real size ourselves, but we'll listen.
  // vivaldi coordinates ignored.
  uint16_t contacts_count;
  // Array of pointers to dynamically-allocated contacts.
  // Should actually be Contact, but we're going to ignore that for clearview.
  struct ContactAddress **contacts;
};

struct RequestFindValuePacket {
  struct RequestPacketHeader header;
  struct VuzeKey key;
  uint32_t flags;
  uint32_t max_values;
};

struct ReplyFindValuePacket {
  struct ReplyPacketHeader header;
  uint8_t has_continuation;
  uint8_t has_values;
  uint16_t contacts_count;
  struct in_addr *contacts;
  //vivaldi coordinates ignored.
  uint8_t diversification_type;
  struct ValueGroup *values;
};

struct ReplyErrorPacket {
  struct ReplyPacketHeader header;
  uint32_t error_type;
  struct ContactAddress sender_address;
  // key blocking ignored.
};

int decode_request(const char *packet, size_t len,
                   struct ClearviewInstance *cv, struct ContactAddress *contact);
int decode_reply(const char *packet, size_t len,
                 struct ClearviewInstance *cv, struct ContactAddress *contact);

// WARNING: Encoding functions are not re-entrant!
void encode_request_header(const struct RequestPacketHeader *header,
                           char buf[REQUEST_HEADER_SIZE]);
void encode_ping_request(const struct RequestPingPacket *packet, char **buf,
                         size_t *buflen);
void encode_find_node_request(const struct RequestFindNodePacket *packet,
                              char **user_buf, size_t *buflen);


void encode_ping_reply(const struct ReplyPingPacket *packet, char **buf,
                       size_t *buflen);
void encode_find_node_reply(const struct ReplyFindNodePacket *packet,
                            char **buf, size_t *buflen);
void encode_store_reply(const struct ReplyStorePacket *packet,
                        char **buf, size_t *buflen);
#endif /* SERIALIZATION_H */
